package common;

import org.junit.Test;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

/**
 * 从 Java 8 引入的一个很有趣的特性是 Optional  类。Optional 类主要解决的问题是臭名昭著的空指针异常（NullPointerException） —— 每个 Java 程序员都非常了解的异常。
 *
 *  本质上，这是一个包含有可选值的包装类，这意味着 Optional 类既可以含有对象也可以为空。
 *   Optional 是 Java 实现函数式编程的强劲一步，并且帮助在范式中实现。但是 Optional 的意义显然不止于此。
 */
public class Example_Optional {

    /**
     *  String isocode = user.getAddress().getCountry().getIsocode().toUpperCase();
     *  要保证没有空指针异常，就需在访问每一个值之前对其进行明确地检查：
     */
//    @Test
//    public void test1(){
//
//        if (user != null) {
//            Address address = user.getAddress();
//            if (address != null) {
//                Country country = address.getCountry();
//                if (country != null) {
//                    String isocode = country.getIsocode();
//                    if (isocode != null) {
//                        isocode = isocode.toUpperCase();
//                    }
//                }
//            }
//        }

    //}
/**

 * 我们来看看用 Optional  类是怎么做的。从创建和验证实例，到使用其不同的方法，并与其它返回相同类型的方法相结合，下面是见证 Optional  奇迹的时刻。
 */
    @Test
    public void test2(){
        User user=null;
        Optional<User> emptyOpt = Optional.empty();
        //emptyOpt.get(); //java.util.NoSuchElementException: No value present
       // Optional<User> opt = Optional.of(user); //java.lang.NullPointerException
        Optional<User> opt1 = Optional.ofNullable(user); //没有报错
        System.out.println(opt1.isPresent());
        opt1.ifPresent( u -> assertEquals(user.getEmail(), u.getEmail()));
        User user1 = opt1.get();
        System.out.println(user1);
    }

    /**
     * ptional 类提供了 API 用以返回对象值，或者在对象为空的时候返回默认值。
     *
     * 这里你可以使用的第一个方法是 orElse()，它的工作方式非常直接，如果有值则返回该值，否则返回传递给它的参数值：
     */
    @Test
    public void whenEmptyValue_thenReturnDefault() {
        User user = null;
        User user2 = new User("anna@gmail.com", "1234");
        User result = Optional.ofNullable(user).orElse(user2);

        assertEquals(user2.getEmail(), result.getEmail());
    }

    /**
     * 第二个同类型的 API 是 orElseGet() —— 其行为略有不同。这个方法会在有值的时候返回值，如果没有值，它会执行作为参数传入的 Supplier(供应者) 函数式接口，并将返回其执行结果：
     */
    @Test
    public void test(){
        User user = null;

        User result = Optional.ofNullable(user).orElseGet( () ->  new User("anna@gmail.com", "1234"));
    }

    /**
     * 返回异常
     * 除了 orElse() 和 orElseGet() 方法，Optional 还定义了 orElseThrow() API —— 它会在对象为空的时候抛出异常，而不是返回备选的值：
     *
     * 这里，如果 user 值为 null，会抛出 IllegalArgumentException。
     * 这个方法让我们有更丰富的语义，可以决定抛出什么样的异常，而不总是抛出 NullPointerException。
     */
    @Test()
    public void whenThrowException_thenOk() {
        User user = null;
        User result = Optional.ofNullable(user)
                .orElseThrow( () -> new IllegalArgumentException());

        System.out.println(result);
    }

    /**
     * 转换值
     * 有很多种方法可以转换 Optional  的值。我们从 map() 和 flatMap() 方法开始。
     * 先来看一个使用 map() API 的例子：
     */
    @Test
    public void whenMap_thenOk() {
        User user = new User("anna@gmail.com", "1234");
        User user1=null;
        String email = Optional.ofNullable(user1)
                .map(u -> u.getEmail()).orElse("default@gmail.com");

      //  assertEquals(email, user.getEmail());
        System.out.println(email);
    }

    /**
     * flatMap 转化值
     * 既然 getter 方法返回 String 值的 Optional，你可以在对 User 的 Optional 对象调用 flatMap() 时，用它作为参数。其返回的值是解除包装的 String 值：
     */
    @Test
    public void whenFlatMap_thenOk() {
        User user = new User("anna@gmail.com", "1234");
        user.setPosition("Developer");
        String position = Optional.ofNullable(user)
                .flatMap(u -> u.getPosition()).orElse("default");

        assertEquals(position, user.getPosition().get());
    }

    /**
     * 过滤值
     * 除了转换值之外，Optional  类也提供了按条件“过滤”值的方法。
     * filter() 接受一个 Predicate 参数，返回测试结果为 true 的值。如果测试结果为 false，会返回一个空的 Optional。
     * 来看一个根据基本的电子邮箱验证来决定接受或拒绝 User(用户) 的示例：
     */
    @Test
    public void whenFilter_thenOk() {
        User user = new User("anna@gmail.com", "1234");
        Optional<User> result = Optional.ofNullable(user)
                .filter(u -> u.getEmail() != null && u.getEmail().contains("@"));

        assertTrue(result.isPresent());
    }

    /**
     * Optional 类的链式方法
     * 为了更充分的使用 Optional，你可以链接组合其大部分方法，因为它们都返回相同类似的对象。
     * 我们使用 Optional  重写最早介绍的示例。
     * 首先，重构类，使其 getter 方法返回 Optional 引用：
     */
    @Test
    public void whenChaining_thenOk() {
        User user = new User("anna@gmail.com", "1234");

        String result = Optional.ofNullable(user)
                .flatMap(u -> u.getAddress())
                .flatMap(a -> a.getCountry())
                .map(c -> c.getIsocode())
                .orElse("default");

        assertEquals(result, "default");
    }

    /**
     * Java 9 增强
       Java 9 为 Optional 类添加了三个方法：or()、ifPresentOrElse() 和 stream()。
     * or() 方法与 orElse() 和 orElseGet() 类似，它们都在对象为空的时候提供了替代情况。or() 的返回值是由 Supplier 参数产生的另一个 Optional 对象。
     * 如果对象包含值，则 Lambda 表达式不会执行：
     */
//    @Test
//    public void whenEmptyOptional_thenGetValueFromOr() {
//        User user = new User("anna@gmail.com", "1234");
//        User result = Optional.ofNullable(user)
//                .or( () -> Optional.of(new User("default","1234"))).get();
//
//        Optional.ofNullable(user).ifPresentOrElse( u -> logger.info("User is:" + u.getEmail()),
//                () -> logger.info("User not found"));
//        assertEquals(result.getEmail(), "default");
//    }
//
//    @Test
//    public void whenGetStream_thenOk() {
//        User user = new User("john@gmail.com", "1234");
//        List<String> emails = Optional.ofNullable(user)
//                .stream()
//                .filter(u -> u.getEmail() != null && u.getEmail().contains("@"))
//                .map( u -> u.getEmail())
//                .collect(Collectors.toList());
//
//        assertTrue(emails.size() == 1);
//        assertEquals(emails.get(0), user.getEmail());
//    }

    class  User{
        private String email;

        private String password;

        private String position;
        private Address address;

        public Optional<Address> getAddress() {
            return Optional.ofNullable(address);
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public void setPosition(String position) {
            this.position = position;
        }

        public Optional<String> getPosition() {
            return Optional.ofNullable(position);
        }

        public User(String email, String password) {
            this.email = email;
            this.password = password;
        }

        public void setEmail(String email) {
            this.email = email;
        }

        public String getEmail() {
            return email;
        }
    }

     class Address {
        private Country country;

        public Optional<Country> getCountry() {
            return Optional.ofNullable(country);
        }

    }

    class Country{
        private String name;
        private String isocode;

        public String getIsocode() {
            return isocode;
        }

        public void setIsocode(String isocode) {
            this.isocode = isocode;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}
